﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Gman.WebApp.Models;

namespace Gman.WebApp.Controllers
{
    public enum SetterFormatOptions
    {
        NotSet,
        None,
        Percent,
        PercentNoReformat,
        Currency,
        PercentTwoDecimal,
        IntegerWithNonNullNonNegReformat,
        USPhoneNumberFormat,
        Notional,
        BPS         
    }

    public static class SetterFormatOptionsHelper
    {
        public static SetterFormatOptions Parse(string setterFormatOptionsString)
        {

            SetterFormatOptions setterFormatOptions = SetterFormatOptions.None;
            try
            {
                if (!string.IsNullOrEmpty(setterFormatOptionsString))
                {
                    setterFormatOptions = (SetterFormatOptions)Enum.Parse(typeof(SetterFormatOptions), setterFormatOptionsString);
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.ToString());
            }
            return setterFormatOptions;
        }

        public static string FormatAsString(object o, SetterFormatOptions setterFormatOptions)
        {
            string formatter = "{0}";
            switch (setterFormatOptions)
            {
                case SetterFormatOptions.PercentNoReformat:
                    formatter = "{0} %";
                    break;
                case SetterFormatOptions.Currency:
                    formatter = "{0:n2}";
                    break;
                case SetterFormatOptions.Percent:
                    formatter = "{0:p5}";
                    break;
                case SetterFormatOptions.PercentTwoDecimal:
                    formatter = "{0:p2}";
                    break;
                case SetterFormatOptions.IntegerWithNonNullNonNegReformat:
                    formatter = "{0:d2}";
                    break;
                case SetterFormatOptions.USPhoneNumberFormat:
                    formatter = "{0}";
                    break;
                case SetterFormatOptions.Notional:
                    formatter = "{0:#,##0}";
                    break;
                case SetterFormatOptions.BPS:
                    formatter = "{0:n3}";
                    break;
            }
            return string.Format(formatter, o);
        }
    }

    internal class PropertyInfoHelper
    {
        public PropertyInfo PropertyInfo { get; set; }
        public object CurrentObject { get; set; }
        public object[] Index { get; set; }

        internal object GetValue()
        {
            return this.PropertyInfo.GetValue(this.CurrentObject, this.Index);
        }

        internal void SetValue(object realValue)
        {
            this.PropertyInfo.SetValue(this.CurrentObject, realValue, this.Index);
        }
    }

    public class PropertySetter<T> //: ActionFilterAttribute
    {
        public class SetterOptions
        {
            public T objectToSet;
            public string property;
            public string val;
            public SetterFormatOptions SetterFormatOptions = SetterFormatOptions.NotSet;
            public List<KeyValuePair<string, string>> validationList;
        }


        /// <summary>
        /// This will return the value that actually got set.  For example if you tried to set characters on a numeric field
        /// it will return null
        /// </summary>
        public static object SetFinalObject(
            SetterOptions options,
            Action<SetterOptions> action)
        {
            try
            {
                object currentObject = options.objectToSet;
                string prop = options.property;
                var propertyInfoHelper = GetFinalObject(currentObject, prop);
                System.ComponentModel.TypeConverter tc = System.ComponentModel.TypeDescriptor.GetConverter(propertyInfoHelper.PropertyInfo.PropertyType);
                options.val = ValCleaner(options.property, options.val, options.SetterFormatOptions);
                try
                {
                    object realValue = null;
                    //special handling here for currencies so that we don't need to tag currencies everywhere
                    //also handles negatives input using parentheses instead of prefixed with a minus sign
                    if (propertyInfoHelper.PropertyInfo.PropertyType == typeof(decimal?) || propertyInfoHelper.PropertyInfo.PropertyType == typeof(decimal))
                    {
                        decimal parsedVal;
                        string cleanedValue = DecimalCleaner(options.val);
                        if (decimal.TryParse(cleanedValue, out parsedVal))
                            realValue = parsedVal;
                        else
                            throw new System.NotSupportedException("Cannot parse the decimal from the input.");
                    }
                    else if (propertyInfoHelper.PropertyInfo.PropertyType == typeof(int?) || propertyInfoHelper.PropertyInfo.PropertyType == typeof(int))
                    {
                        realValue = Convert.ToInt32(options.val);
                    }
                    else if (propertyInfoHelper.PropertyInfo.PropertyType == typeof(Int64?) || propertyInfoHelper.PropertyInfo.PropertyType == typeof(Int64))
                    {
                        realValue = Convert.ToInt64(options.val);
                    }
                    else if (propertyInfoHelper.PropertyInfo.PropertyType == typeof(DateTime?) || propertyInfoHelper.PropertyInfo.PropertyType == typeof(DateTime))
                    {
                        realValue = Convert.ToDateTime(options.val);
                    }
                    else if (options.val.Equals("Seleccione"))
                    {
                        propertyInfoHelper.SetValue(null);
                    }
                    else
                    {
                        realValue = options.val;
                    }
                    propertyInfoHelper.SetValue(realValue);
                    return propertyInfoHelper.GetValue();
                }
                catch
                {
                    propertyInfoHelper.SetValue(null);
                    throw;
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                if (action != null)
                {
                    action(options);
                }
                return null;
            }
        }

        internal static PropertyInfoHelper GetFinalObject(object currentObject, string prop)
        {
            if (prop.Contains("."))
            {
                string[] propertyList = prop.Split('.');
                for (int i = 0; i < propertyList.Length - 1; i++)
                {
                    prop = propertyList[i + 1];
                    string currentProp = propertyList[i];
                    currentObject = GetPropertyValue(currentProp, currentObject);
                }
            }
            var propertyInfoHelper = GetPropertyInfo(prop, currentObject);
            return propertyInfoHelper;
        }

        private static object GetPropertyValue(string propertyName, object currentObject)
        {
            var propertyInfoHelper = GetPropertyInfo(propertyName, currentObject);
            return propertyInfoHelper.GetValue();
        }

        private static PropertyInfoHelper GetPropertyInfo(string propertyName, object currentObject)
        {
            PropertyInfoHelper retval = new PropertyInfoHelper();
            var propertyArraySplit = propertyName.Split(new char[] { '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
            if (propertyArraySplit.Length == 1)
            {
                retval.PropertyInfo = currentObject.GetType().GetProperty(propertyName);
                retval.CurrentObject = currentObject;
            }
            else if (propertyArraySplit.Length == 2)
            {
                propertyName = propertyArraySplit[0];
                int index = int.Parse(propertyArraySplit[1]);
                var propertyInfo = currentObject.GetType().GetProperty(propertyName);
                // Get collection
                currentObject = propertyInfo.GetValue(currentObject, null);
                string indexerName = ((DefaultMemberAttribute)currentObject.GetType()
                    .GetCustomAttributes(typeof(DefaultMemberAttribute), true)[0]).MemberName;
                // Get element in collection
                retval.PropertyInfo = currentObject.GetType().GetProperty(indexerName);
                retval.Index = new object[] { index };
                retval.CurrentObject = currentObject;

            }
            else
            {
                throw new NotSupportedException("Multi dimensional arrays are not supported");
            }
            return retval;
        }

        public static object SetFinalObject(SetterOptions options)
        {
            return SetFinalObject(options, null);
        }

        public static string ValCleaner(string property, string val, SetterFormatOptions setterFormatOptions)
        {
            switch (setterFormatOptions)
            {
                case SetterFormatOptions.Currency:
                    return CurrencyValCleaner(val);
                case SetterFormatOptions.BPS:
                    return BPSValCleaner(val);
                case SetterFormatOptions.PercentNoReformat:
                    return PercentCleaner(val);
                case SetterFormatOptions.Percent:
                    return PercentCleanerWithReformat(val);
                case SetterFormatOptions.PercentTwoDecimal:
                    return PercentCleanerWithReformat(val);
                case SetterFormatOptions.IntegerWithNonNullNonNegReformat:
                    return IntegerCleanerNonNullReformat(val);
                case SetterFormatOptions.USPhoneNumberFormat:
                    return CleanUSPhoneNumber(val);
                case SetterFormatOptions.None:
                case SetterFormatOptions.NotSet:
                    return val;
                default:
                    throw new NotImplementedException(string.Format("SetterFormatOptions {0} has not been implemented", setterFormatOptions));
            }

        }

        private static string DecimalCleaner(string val)
        {
            val = NegativeCleaner(val);
            val = PercentCleaner(val);

            return val;
        }

        private static string NegativeCleaner(string val)
        {
            if (val != null && val.Contains("(") && val.Contains(")"))
            {
                val = val.Replace('(', '-').Replace(")", string.Empty);
            }
            return val;
        }

        public static string PercentCleanerWithReformat(string val)
        {
            decimal reformatValue;
            if (decimal.TryParse(PercentCleaner(val), out reformatValue))
            {
                return (reformatValue / 100).ToString();
            }
            return null;
        }

        private static string CleanUSPhoneNumber(string val)
        {
            if (ValidationBase.ValidatePhoneNumberFormat(val))
            {
                return val;
            }
            else
            {
                return System.String.Empty;
            }
        }

        private static string IntegerCleanerNonNullReformat(string val)
        {
            if (System.String.IsNullOrEmpty(val))
            {
                return "0";
            }
            else
            {
                int tempval = 0;

                bool canConvert = Int32.TryParse(val, out tempval);

                if (canConvert)
                {
                    if (tempval > 0)
                    {
                        return val;
                    }
                    else
                    {
                        return "0";
                    }
                }
                else
                {
                    return "0";
                }

            }
        }

        private static string CleanByName(string property, string val)
        {
            if (property == "PotentialFutureExposure")
                return CurrencyValCleaner(val);
            if (property.ToLower().Contains("multiplier"))
                return PercentCleaner(val);
            if (property == "ResidualPercent")
                return PercentCleaner(val);
            return val;
        }

        public static string PercentCleaner(string val)
        {
            return val.Replace("%", "");
        }
        public static string CurrencyValCleaner(string val)
        {
            decimal d;
            if (decimal.TryParse(val, System.Globalization.NumberStyles.Currency, System.Globalization.CultureInfo.CurrentCulture, out d))
            {
                return string.Format("{0:n2}",d);
            }
            return val;
        }
       
        public static string BPSValCleaner(string val)
        {
            decimal d;
            if (decimal.TryParse(val, out d))
            {
                return string.Format("{0:n3}", d);
            }
            return val;
        }
    }

}
